package com.skyd.core.vector;

import java.util.ArrayList;

import com.skyd.core.math.MathEx;

/**
 * 二维向量类。 <i>移植于Robert Penner的AS代码。</i>
 * 
 * @author SkyD
 * 
 */
public class Vector2D {

	/**
	 * 重设
	 * 
	 * @param x
	 *            X值
	 * @param y
	 *            Y值
	 * @return 自身
	 */
	public Vector2D reset(double x, double y) {
		setX(x);
		setY(y);
		return this;
	}

	/**
	 * 设置为与目标相同的值
	 * 
	 * @param x
	 *            X值
	 * @param y
	 *            Y值
	 * @return 自身
	 */
	public Vector2D resetWith(Vector2D value) {
		setX(value.getX());
		setY(value.getY());
		return this;
	}

	/**
	 * 获取整数形式的X值，取整方式为Ceil，例如2.012取整为3
	 * 
	 * @return
	 */
	public int getIntX() {
		return (int) Math.ceil(getX());
	}

	/**
	 * 获取整数形式的Y值，取整方式为Ceil，例如2.012取整为3
	 * 
	 * @return
	 */
	public int getIntY() {
		return (int) Math.ceil(getY());
	}

	/**
	 * 初始值为0。 功能注释：X坐标
	 */
	protected double _X = 0;

	/**
	 * 获取X。 功能注释：X坐标
	 * 
	 * @return 当前的double类型的值
	 */
	public double getX() {
		return _X;
	}

	/**
	 * 设置X。 功能注释：X坐标
	 * 
	 * @param value
	 *            要设置的double类型值
	 * @return 自身
	 */
	public Vector2D setX(double value) {
		if (!onXChanging(_X, value))
			return this;
		_X = value;
		onValueChanged(_X, _Y);
		return this;
	}

	/**
	 * 设置X为其默认值(0)。 功能注释：X坐标
	 * 
	 * @return 自身
	 */
	public Vector2D setXToDefault() {
		setX(0);
		return this;
	}

	/**
	 * 初始值为0。 功能注释：Y坐标
	 */
	protected double _Y = 0;

	/**
	 * 获取Y。 功能注释：Y坐标
	 * 
	 * @return 当前的double类型的值
	 */
	public double getY() {
		return _Y;
	}

	/**
	 * 设置Y。 功能注释：Y坐标
	 * 
	 * @param value
	 *            要设置的double类型值
	 * @return 自身
	 */
	public Vector2D setY(double value) {
		if (!onYChanging(_Y, value))
			return this;
		_Y = value;
		onValueChanged(_X, _Y);
		return this;
	}

	/**
	 * 设置Y为其默认值(0)。 功能注释：Y坐标
	 * 
	 * @return 自身
	 */
	public Vector2D setYToDefault() {
		setY(0);
		return this;
	}

	/**
	 * 构造函数
	 * 
	 * @param x
	 *            X坐标
	 * @param y
	 *            Y坐标
	 */
	public Vector2D(double x, double y) {
		setX(x);
		setY(y);
	}
	
	/**
	 * 构造函数
	 */
	public Vector2D(){
		
	}
	
	/**
	 * 构造函数
	 * @param array 下标0和1分别代表X、Y
	 */
	public Vector2D(double[] array){
		setX(array[0]);
		setY(array[1]);
	}
	
	/**
	 * 返回数组，下标0和1分别代表X、Y
	 * @return 数组
	 */
	public double[] toArray(){
		return new double[]{getX(),getY()};
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		return getClone();
	}

	/**
	 * 获取一个克隆副本
	 * 
	 * @return 副本
	 */
	public Vector2D getClone() {
		return new Vector2D(_X, _Y);
	}

	@Override
	public String toString() {
		return "[" + Math.round(this._X * 1000) / 1000 + ","
				+ Math.round(this._Y * 1000) / 1000 + "]";
	}

	/**
	 * 加运算
	 * 
	 * @param value
	 *            相加的对象
	 * @return 自身
	 */
	public Vector2D plus(Vector2D value) {
		setX(_X + value._X);
		setY(_Y + value._Y);
		return this;
	}

	/**
	 * 返回一个新对象用于存放加运算结果
	 * 
	 * @param value
	 *            相加的对象
	 * @return 结果对象
	 */
	public Vector2D plusNew(Vector2D value) {
		return new Vector2D(_X + value._X, _Y + value._Y);
	}

	/**
	 * 减运算
	 * 
	 * @param value
	 *            相减的对象
	 * @return 自身
	 */
	public Vector2D minus(Vector2D value) {
		setX(_X - value._X);
		setY(_Y - value._Y);
		return this;
	}

	/**
	 * 返回一个新对象用于存放减运算结果
	 * 
	 * @param value
	 *            相减的对象
	 * @return 结果对象
	 */
	public Vector2D minusNew(Vector2D value) {
		return new Vector2D(_X - value._X, _Y - value._Y);
	}

	/**
	 * 负运算，获得相反数
	 * 
	 * @return 自身
	 */
	public Vector2D negate() {
		setX(-_X);
		setY(-_Y);
		return this;
	}

	/**
	 * 返回一个新对象用于存放负运算结果
	 * 
	 * @return 结果对象
	 */
	public Vector2D negateNew() {
		return new Vector2D(-_X, -_Y);
	}

	/**
	 * 缩放，即乘运算
	 * 
	 * @param value
	 *            乘算值
	 * @return 自身
	 */
	public Vector2D scale(double value) {
		scale(value, value);
		return this;
	}

	/**
	 * 返回一个新对象用于存放乘运算（缩放）结果
	 * 
	 * @param value
	 *            乘算值
	 * @return 结果对象
	 */
	public Vector2D scaleNew(double value) {
		return scaleNew(value, value);
	}

	/**
	 * 缩放，即乘运算
	 * 
	 * @param xValue
	 *            X轴乘算值
	 * @param yValue
	 *            Y轴乘算值
	 * @return 自身
	 */
	public Vector2D scale(double xValue, double yValue) {
		setX(_X * xValue);
		setY(_Y * yValue);
		return this;
	}

	/**
	 * 缩放，即乘运算
	 * 
	 * @param value
	 *            缩放值
	 * @return 自身
	 */
	public Vector2D scale(Vector2D value) {
		return scale(value.getX(), value.getY());
	}

	/**
	 * 返回一个新对象用于存放乘运算（缩放）结果
	 * 
	 * @param xValue
	 *            X轴乘算值
	 * @param yValue
	 *            Y轴乘算值
	 * @return 结果对象
	 */
	public Vector2D scaleNew(double xValue, double yValue) {
		return new Vector2D(_X * xValue, _Y * yValue);
	}

	/**
	 * 返回一个新对象用于存放乘运算（缩放）结果
	 * 
	 * @param value
	 *            缩放值
	 * @return 结果对象
	 */
	public Vector2D scaleNew(Vector2D value) {
		return scaleNew(value.getX(), getY());
	}

	/**
	 * 根据指定的中心点缩放，即乘运算
	 * 
	 * @param xValue
	 *            X轴乘算值
	 * @param yValue
	 *            Y轴乘算值
	 * @param center
	 *            参照中心点
	 * @return 自身
	 */
	public Vector2D scale(double xValue, double yValue, Vector2D center) {
		minus(center);
		setX(_X * xValue);
		setY(_Y * yValue);
		plus(center);
		return this;
	}

	/**
	 * 根据指定的中心点缩放，即乘运算
	 * 
	 * @param value
	 *            缩放值
	 * @param center
	 *            参照中心点
	 * @return 自身
	 */
	public Vector2D scale(Vector2D value, Vector2D center) {
		return scale(value.getX(), value.getY(), center);
	}

	/**
	 * 返回一个新对象用于存放根据指定的中心点的乘运算（缩放）结果
	 * 
	 * @param xValue
	 *            X轴乘算值
	 * @param yValue
	 *            Y轴乘算值
	 * @param center
	 *            参照中心点
	 * @return 结果对象
	 */
	public Vector2D scaleNew(double xValue, double yValue, Vector2D center) {
		Vector2D v = getClone();
		v.scale(xValue, yValue, center);
		return v;
	}

	/**
	 * 返回一个新对象用于存放根据指定的中心点的乘运算（缩放）结果
	 * 
	 * @param value
	 *            缩放值
	 * @param center
	 *            参照中心点
	 * @return 结果对象
	 */
	public Vector2D scaleNew(Vector2D value, Vector2D center) {
		return scaleNew(value.getX(), value.getY(), center);
	}

	/**
	 * 获取向量长度，也称为向量的绝对值或模。根据X、Y的值，利用勾股定理求得。通常可以用来获取速率。
	 * 
	 * @return 长度
	 * @return 自身
	 */
	public double getLength() {
		return (double) Math.sqrt(_X * _X + _Y * _Y);
	}

	/**
	 * 在不改变向量方向的前提下，重设向量的长度。通常可用于设置速率。
	 * <p>
	 * <b>注意，如果当前长度为0时，则该向量没有方向，那么设置长度后将会使X值等于设定的长度，Y值等于0</b>
	 * </p>
	 * 
	 * @param value
	 *            重设的长度值
	 * @return 自身
	 */
	public Vector2D setLength(double value) {
		double r = this.getLength();
		if (r != 0)
			this.scale(value / r);
		else
			setX(value);
		return this;
	}

	/**
	 * 获取向量方向角度
	 * 
	 * @return 角度
	 */
	public double getAngle() {
		return MathEx.atan2Angle(_Y, _X);
	}

	/**
	 * 在不改变向量长度的前提下，重设向量方向角度。
	 * 
	 * @param angle
	 *            新角度值，一个0至360之间的值
	 * @return 自身
	 */
	public Vector2D setAngle(double angle) {
		double r = getLength();
		setX(r * MathEx.cosAngle(angle));
		setY(r * MathEx.sinAngle(angle));
		return this;
	}

	/**
	 * 以当前角度为基础，旋转指定角度
	 * 
	 * @param angle
	 *            旋转角度值，一个-360至360之间的值
	 * @return 自身
	 */
	public Vector2D rotate(double angle) {
		double ca = MathEx.cosAngle(angle);
		double sa = MathEx.sinAngle(angle);
		double rx = _X * ca - _Y * sa;
		double ry = _X * sa + _Y * ca;
		setX(rx);
		setY(ry);
		return this;
	}

	/**
	 * 返回一个经过旋转的新的结果对象
	 * 
	 * @param angle
	 *            旋转角度值，一个-360至360之间的值
	 * @return 结果对象
	 */
	public Vector2D rotateNew(double angle) {
		Vector2D v = getClone();
		v.rotate(angle);
		return v;
	}

	/**
	 * 以当前角度为基础，围绕指定中心点旋转指定角度
	 * 
	 * @param angle
	 *            旋转角度值，一个-360至360之间的值
	 * @param center
	 *            参照中心点
	 * @return 自身
	 */
	public Vector2D rotate(double angle, Vector2D center) {
		minus(center);
		rotate(angle);
		plus(center);
		return this;
	}

	/**
	 * 以当前角度为基础，返回围绕指定中心点旋转指定角度后的新对象
	 * 
	 * @param angle
	 *            旋转角度值，一个-360至360之间的值
	 * @param center
	 *            参照中心点
	 * @return 结果对象
	 */
	public Vector2D rotateNew(double angle, Vector2D center) {
		Vector2D v = getClone();
		v.rotate(angle, center);
		return v;
	}

	/**
	 * 将两轴分别乘算并相加以获得点积（数量积）。如果两个向量点积为0，则它们互相垂直。
	 * 
	 * @param v
	 *            乘算对象
	 * @return 点积
	 */
	public double dot(Vector2D v) {
		return _X * v._X + _Y * v._Y;
	}

	/**
	 * 判断是否垂直于目标向量
	 * 
	 * @param v
	 *            检测垂直的目标向量
	 * @return 是否垂直
	 */
	public boolean isPerpTo(Vector2D v) {
		return dot(v) == 0;
	}

	/**
	 * 获得顺时针法向量，即与当前向量成90°夹角的向量。通常在力学计算中使用。
	 * 
	 * @return 法向量对象
	 */
	public Vector2D getNormalCW() {
		return new Vector2D(_Y, -_X);
	}
	
	/**
	 * 获得逆时针法向量，即与当前向量成90°夹角的向量。通常在力学计算中使用。
	 * 
	 * @return 法向量对象
	 */
	public Vector2D getNormalCCW() {
		return new Vector2D(-_Y, _X);
	}

	/**
	 * 计算向量间夹角的角度
	 * 
	 * @param v
	 *            目标向量
	 * @return 角度值，一个0至360之间的值
	 */
	public double angleBetween(Vector2D v) {
		double dp = dot(v);
		double cosAngle = dp / (getLength() * v.getLength());
		return MathEx.acosAngle(cosAngle);
	}

	/**
	 * 当X即将更改时触发，返回false表示中止此更改
	 */
	public interface OnXChangingListener {
		/**
		 * 触发XChanging事件时执行的方法。事件功能注释：当X即将更改时触发，返回false表示中止此更改
		 * 
		 * @param sender
		 *            触发XChanging事件的对象
		 */
		public abstract boolean OnXChangingEvent(Object sender,
				double currentValue, double newValue);
	}

	/**
	 * XChanging事件监听器列表。当X即将更改时触发，返回false表示中止此更改
	 */
	private ArrayList<OnXChangingListener> _XChangingListenerList = null;

	/**
	 * 增加一个XChanging事件的监听器，返回是否成功增加。事件功能注释：当X即将更改时触发，返回false表示中止此更改
	 * 
	 * @param listener
	 *            XChanging事件监听器对象
	 * @return 是否成功增加。如果该监听器对象已存在，则返回false。
	 */
	public boolean addOnXChangingListener(OnXChangingListener listener) {
		if (_XChangingListenerList == null)
			_XChangingListenerList = new ArrayList<OnXChangingListener>();
		else if (_XChangingListenerList.contains(listener))
			return false;
		_XChangingListenerList.add(listener);
		return true;
	}

	/**
	 * 移除一个XChanging事件的监听器，返回是否成移除。事件功能注释：当X即将更改时触发，返回false表示中止此更改
	 * 
	 * @param listener
	 *            XChanging事件监听器对象
	 * @return 是否成功移除。如果该监听器对象不存在，则返回false。
	 */
	public boolean removeOnXChangingListener(OnXChangingListener listener) {
		if (_XChangingListenerList == null
				|| !_XChangingListenerList.contains(listener))
			return false;
		_XChangingListenerList.remove(listener);
		return true;
	}

	/**
	 * 清空所有XChanging事件的监听器。事件功能注释：当X即将更改时触发，返回false表示中止此更改
	 */
	public void clearOnXChangingListeners() {
		if (_XChangingListenerList != null)
			_XChangingListenerList.clear();
	}

	/**
	 * 当X即将更改时触发，返回false表示中止此更改
	 */
	protected boolean onXChanging(double currentValue, double newValue) {
		if (_XChangingListenerList != null)
			for (OnXChangingListener listener : _XChangingListenerList) {
				if (!listener.OnXChangingEvent(this, currentValue, newValue))
					return false;
			}
		return true;
	}

	/**
	 * 当Y即将更改时触发，返回false表示中止此更改
	 */
	public interface OnYChangingListener {
		/**
		 * 触发YChanging事件时执行的方法。事件功能注释：当Y即将更改时触发，返回false表示中止此更改
		 * 
		 * @param sender
		 *            触发YChanging事件的对象
		 */
		public abstract boolean OnYChangingEvent(Object sender,
				double currentValue, double newValue);
	}

	/**
	 * YChanging事件监听器列表。当Y即将更改时触发，返回false表示中止此更改
	 */
	private ArrayList<OnYChangingListener> _YChangingListenerList = null;

	/**
	 * 增加一个YChanging事件的监听器，返回是否成功增加。事件功能注释：当Y即将更改时触发，返回false表示中止此更改
	 * 
	 * @param listener
	 *            YChanging事件监听器对象
	 * @return 是否成功增加。如果该监听器对象已存在，则返回false。
	 */
	public boolean addOnYChangingListener(OnYChangingListener listener) {
		if (_YChangingListenerList == null)
			_YChangingListenerList = new ArrayList<OnYChangingListener>();
		else if (_YChangingListenerList.contains(listener))
			return false;
		_YChangingListenerList.add(listener);
		return true;
	}

	/**
	 * 移除一个YChanging事件的监听器，返回是否成移除。事件功能注释：当Y即将更改时触发，返回false表示中止此更改
	 * 
	 * @param listener
	 *            YChanging事件监听器对象
	 * @return 是否成功移除。如果该监听器对象不存在，则返回false。
	 */
	public boolean removeOnYChangingListener(OnYChangingListener listener) {
		if (_YChangingListenerList == null
				|| !_YChangingListenerList.contains(listener))
			return false;
		_YChangingListenerList.remove(listener);
		return true;
	}

	/**
	 * 清空所有YChanging事件的监听器。事件功能注释：当Y即将更改时触发，返回false表示中止此更改
	 */
	public void clearOnYChangingListeners() {
		if (_YChangingListenerList != null)
			_YChangingListenerList.clear();
	}

	/**
	 * 当Y即将更改时触发，返回false表示中止此更改
	 */
	protected boolean onYChanging(double currentValue, double newValue) {
		if (_YChangingListenerList != null)
			for (OnYChangingListener listener : _YChangingListenerList) {
				if (!listener.OnYChangingEvent(this, currentValue, newValue))
					return false;
			}
		return true;
	}

	/**
	 * 当值改变时触发
	 */
	public interface OnValueChangedListener {
		/**
		 * 触发ValueChanged事件时执行的方法
		 * 
		 * @param sender
		 *            触发ValueChanged事件的对象
		 */
		public abstract void OnValueChangedEvent(Object sender, double newX,
				double newY);
	}

	/**
	 * ValueChanged事件监听器列表。当值改变时触发
	 */
	private ArrayList<OnValueChangedListener> _ValueChangedListenerList = null;

	/**
	 * 增加一个ValueChanged事件的监听器，返回是否成功增加。事件功能注释：当值改变时触发
	 * 
	 * @param listener
	 *            ValueChanged事件监听器对象
	 * @return 是否成功增加。如果该监听器对象已存在，则返回false。
	 */
	public boolean addOnValueChangedListener(OnValueChangedListener listener) {
		if (_ValueChangedListenerList == null)
			_ValueChangedListenerList = new ArrayList<OnValueChangedListener>();
		else if (_ValueChangedListenerList.contains(listener))
			return false;
		_ValueChangedListenerList.add(listener);
		return true;
	}

	/**
	 * 移除一个ValueChanged事件的监听器，返回是否成移除。事件功能注释：当值改变时触发
	 * 
	 * @param listener
	 *            ValueChanged事件监听器对象
	 * @return 是否成功移除。如果该监听器对象不存在，则返回false。
	 */
	public boolean removeOnValueChangedListener(OnValueChangedListener listener) {
		if (_ValueChangedListenerList == null
				|| !_ValueChangedListenerList.contains(listener))
			return false;
		_ValueChangedListenerList.remove(listener);
		return true;
	}

	/**
	 * 清空所有ValueChanged事件的监听器。事件功能注释：当值改变时触发
	 */
	public void clearOnValueChangedListeners() {
		if (_ValueChangedListenerList != null)
			_ValueChangedListenerList.clear();
	}

	/**
	 * 当值改变时触发
	 */
	protected void onValueChanged(double newX, double newY) {
		if (_ValueChangedListenerList != null)
			for (OnValueChangedListener listener : _ValueChangedListenerList) {
				listener.OnValueChangedEvent(this, newX, newY);
			}
	}
	
	/**
	 * 判断向量是否位于目标矩形中
	 */
	public boolean isIn(VectorRect2DF r)
	{
		return _X>=r.getLeft()&&_X<=r.getRight()&&_Y>=r.getTop()&&_Y<=r.getBottom();
	}
	
	/**
	 * 增加X向量
	 * @param v 值
	 * @return 自身
	 */
	public Vector2D plusX(double v){
		setX(_X+v);
		return this;
	}
	
	/**
	 * 增加Y向量
	 * @param v 值
	 * @return 自身
	 */
	public Vector2D plusY(double v){
		setY(_Y+v);
		return this;
	}
	
	/**
	 * 缩放X向量
	 * @param v 值
	 * @return 自身
	 */
	public Vector2D scaleX(double v){
		setX(_X*v);
		return this;
	}
	
	/**
	 * 缩放Y向量
	 * @param v 值
	 * @return 自身
	 */
	public Vector2D scaleY(double v){
		setY(_Y*v);
		return this;
	}
	
	/**
	 * 约束长度，即如果自身长度大于给定长度，则重设长度为给定值
	 * @param v 值
	 * @return 自身
	 */
	public Vector2D restrainLength(double v){
		if(getLength()>v)
			setLength(v);
		return this;
	}
	
	/**
	 * 将向量设为自身与目标向量的均值
	 * @param v 目标向量
	 * @return 自身
	 */
	public Vector2D averageWith(Vector2D v){
		setX((_X+v._X)/2);
		setY((_Y+v._Y)/2);
		return this;
	}
	
	/**
	 * 将一个新向量设为此向量与目标向量的均值
	 * @param v 目标向量
	 * @return 新的向量
	 */
	public Vector2D averageNewWith(Vector2D v){
		return this.getClone().averageWith(v);
	}
}
